import math

from PyQt6.QtCore import pyqtSignal, QPointF, QEvent, Qt
from PyQt6.QtGui import QMouseEvent
from PyQt6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsEllipseItem, QGraphicsRectItem, \
    QGraphicsLineItem, QGraphicsTextItem

from shape import Shapes


class BoardGraphicsView(QGraphicsView):
    # 鼠标移动时触发
    mousemoved = pyqtSignal(QPointF)
    selectionChanged = pyqtSignal(QGraphicsItem)
    clearSelection = pyqtSignal()

    def __init__(self, parent=None):
        super(BoardGraphicsView, self).__init__(parent)
        # 允许在不按键的情况下跟踪鼠标
        self.setMouseTracking(True)
        # 允许响应拖拽释放
        self.setAcceptDrops(True)

        # 释放处鼠标位置
        self.dropPoint = QPointF(0, 0)
        # 等待调整大小的图元
        self.resizeItem = None
        # 等待调整位置的图元
        self.moveItem = None

    def draw(self, item: QGraphicsItem):
        item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
        item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsFocusable)
        item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
        self.scene().addItem(item)

    def mouseMoveEvent(self, event: QMouseEvent):
        # 触发鼠标移动信号
        self.mousemoved.emit(event.position())
        # 拦截拖拽调整模式
        if self.resizeItem is not None:
            self._resizeItem(self.resizeItem, event.position())
            super().mouseMoveEvent(event)
        elif self.moveItem is not None:
            self._moveItem(self.moveItem, event.position())
            super().mouseMoveEvent(event)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            # 检查是否进入拖拽模式
            sp = self.mapToScene(event.position().toPoint())
            item = self.scene().itemAt(sp, self.transform())
            if item is None:
                # 检查附近是否存在直线
                offx = -10
                offy = -10
                for _ in range(100):
                    offx += 0.2
                    for _ in range(10):
                        offy += 0.2
                        nsp = sp + QPointF(offx, offy)
                        nitem = self.scene().itemAt(nsp, self.transform())
                        if isinstance(item, QGraphicsLineItem):
                            item = nitem

            if item is not None:
                self.selectionChanged.emit(item)
                ip = item.mapFromScene(sp)
                if isinstance(item, QGraphicsEllipseItem):
                    # 圆形
                    d = math.sqrt(ip.x() ** 2 + ip.y() ** 2)
                    if d >= (item.boundingRect().width() / 2) * 0.8:
                        self.resizeItem = item
                    else:
                        self.moveItem = item
                elif isinstance(item, QGraphicsRectItem):
                    # 矩形
                    if abs(ip.x()) >= (item.boundingRect().width() / 2) * 0.8 \
                            and abs(ip.y()) >= (item.boundingRect().height() / 2) * 0.8:
                        self.resizeItem = item
                    else:
                        self.moveItem = item
                elif isinstance(item, QGraphicsLineItem):
                    line = item.line()
                    dp = line.p1() - ip
                    d = math.sqrt(dp.x() ** 2 + dp.y() ** 2)
                    if d >= 10:
                        self.resizeItem = item
                    if self.resizeItem is None:
                        dp = line.p2() - ip
                        d = math.sqrt(dp.x() ** 2 + dp.y() ** 2)
                        if d >= 10:
                            self.resizeItem = item
                    if self.resizeItem is None:
                        self.moveItem = item
                elif isinstance(item, QGraphicsTextItem):
                    # 文本
                    self.moveItem = item
            else:
                self.clearSelection.emit()
        super().mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        self.resizeItem = None
        self.moveItem = None
        super().mouseReleaseEvent(event)

    def dragEnterEvent(self, event: QEvent):
        # 将事件移交至 BoardGraphicsScene
        event.accept()

    def dropEvent(self, event):
        self.dropPoint = event.position()
        self.setFocus()

    def _resizeItem(self, item: QGraphicsItem, point: QPointF):
        # 鼠标位置 -> 场景坐标（相对于场景坐标系原点）
        sp = self.mapToScene(point.toPoint())
        # 鼠标位置 -> 局部坐标（相对于图元）
        ip = item.mapFromScene(sp)
        if isinstance(item, QGraphicsEllipseItem):
            # 圆形
            d = math.sqrt(ip.x() ** 2 + ip.y() ** 2)
            rect = item.boundingRect()
            rect.setRect(-d, -d, 2 * d, 2 * d)
            item.setRect(rect)
        elif isinstance(item, QGraphicsRectItem):
            # 矩形
            w = abs(ip.x())
            h = abs(ip.y())
            rect = item.boundingRect()
            rect.setRect(-w, -h, 2 * w, 2 * h)
            item.setRect(rect)
        elif isinstance(item, QGraphicsLineItem):
            # 直线
            self.scene().removeItem(item)
            line = item.line()
            p1 = line.p1()
            p2 = line.p2()
            dp1 = p1 - ip
            dp2 = p2 - ip
            d1 = math.sqrt(dp1.x() ** 2 + dp1.y() ** 2)
            d2 = math.sqrt(dp2.x() ** 2 + dp2.y() ** 2)
            if d1 < d2:
                newItem = Shapes.newLine(sp, item.zValue(), ip.x(), ip.y(), p2.x(), p2.y())
            else:
                newItem = Shapes.newLine(sp, item.zValue(), p1.x(), p1.y(), ip.x(), ip.y())
            newItem.setScale(item.scale())
            newItem.setRotation(item.rotation())
            newItem.setPen(item.pen())
            self.scene().addItem(newItem)
            self.resizeItem = newItem
            self.selectionChanged.emit(newItem)

    def _moveItem(self, item: QGraphicsItem, point: QPointF):
        # 鼠标位置 -> 场景坐标（相对于场景坐标系原点）
        sp = self.mapToScene(point.toPoint())
        item.setPos(sp)


class BoardGraphicsScene(QGraphicsScene):
    def __init__(self, sceneRect):
        super(BoardGraphicsScene, self).__init__(sceneRect)

    def dragMoveEvent(self, event):
        # 接受 BoardGraphicsView 转移过来的事件
        event.accept()

    def mousePressEvent(self, event):
        pass
